<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="shortcut icon" href="data:;base64,iVBORw0KGgo=">
  <title>WebGPU Demo - step 2 - Pipeline</title>
  <style>
    html,
    body {
      margin: 0;
      overflow: hidden;
      height: 100%;
    }
  </style>
</head>

<body>
  <canvas id="canvas"></canvas>
  <script type="module">
    import { GUI } from '../js/vendor/dat.gui.js';
    import glslangModule from '../js/vendor/glslang.js';

    (async () => {
      if (!navigator.gpu) {
        const div = document.createElement('div');
        div.innerHTML = '<div>Your browser does not support WebGPU. Go to <a href="https://webgpu.io">webgpu.io</a> for more information.</div>';
        document.body.insertBefore(div, canvas);
        return;
      }

      // Initialization
      const adapter = await navigator.gpu.requestAdapter();
      const device = await adapter.requestDevice();
      const context = canvas.getContext('gpupresent');
      const format = await context.getSwapChainPreferredFormat(device);
      const swapChain = context.configureSwapChain({ device, format });

      const glslang = await glslangModule();

      const vertexShader = `#version 450
        const vec2 c_position[3] = vec2[](
          vec2(0.0, 0.0),
          vec2(1.0, 0.0),
          vec2(0.0, 1.0)
        );

        void main () {
          gl_Position = vec4(c_position[gl_VertexIndex] - 0.5, 0.0, 1.0);
        }
      `;
      const fragmentShader = `#version 450
        layout(location = 0) out vec4 fragColor;

        void main () {
          fragColor = vec4(1.0);
        }
      `;

      // Create Render Pipeline
      const pipeline = device.createRenderPipeline({
        vertexStage: {
          module: device.createShaderModule({
            code: glslang.compileGLSL(vertexShader, 'vertex'),
          }),
          entryPoint: 'main',
        },
        fragmentStage: {
          module: device.createShaderModule({
            code: glslang.compileGLSL(fragmentShader, 'fragment'),
          }),
          entryPoint: 'main',
        },
        primitiveTopology: 'triangle-list',
        colorStates: [{ format }],
      });

      const passDescriptor = {
        colorAttachments: [{
          attachment: undefined,
          loadValue: [0, 0, 0, 1],
        }],
      };

      function resize() {
        const width = window.innerWidth;
        const height = window.innerHeight;
        const { devicePixelRatio } = window;
        canvas.width = width * devicePixelRatio;
        canvas.height = height * devicePixelRatio;
        canvas.style.width = `${width}px`;
        canvas.style.height = `${height}px`;
      }

      window.onresize = resize;
      resize();

      // Change background color
      const gui = new GUI();
      const config = {
        backgroundColor: [0, 0, 0],
      };
      gui.addColor(config, 'backgroundColor').onChange((value) => {
        passDescriptor.colorAttachments[0].loadValue[0] = value[0] / 255;
        passDescriptor.colorAttachments[0].loadValue[1] = value[1] / 255;
        passDescriptor.colorAttachments[0].loadValue[2] = value[2] / 255;
      });

      function draw(commandEncoder) {
        const passEncoder = commandEncoder.beginRenderPass(passDescriptor);
        passEncoder.setPipeline(pipeline);
        passEncoder.draw(3);
        passEncoder.endPass();
      }

      function render() {
        const textureView = swapChain.getCurrentTexture().createView();
        passDescriptor.colorAttachments[0].attachment = textureView;
        const commandEncoder = device.createCommandEncoder();
        draw(commandEncoder);
        device.defaultQueue.submit([commandEncoder.finish()]);
        requestAnimationFrame(render);
      }

      requestAnimationFrame(render);
    })();
  </script>
</body>

</html>